#ifndef __SSDSIM_CACHE__
#define __SSDSIM_CACHE__
#include "ssdsim_global.h"
#include <set>
#include <queue>
#include <map>

#define CACHE_SHIFT 12
#define CACHE_ADDR(x) (x) >> CACHE_SHIFT
#define LRU 1
#define LFU 2
///////////////////////////
//////utility
#define NOT_FOUND -1
///////////////////////////
//Delay
#define CACHE_ACCESS_DELAY 0.004 ms
///////////////////////////
class Cache_info{
public:
	int lbn;
	int valid;
	int dirty;
	int age;

};
class Cache_stat{
public:
	int cache_hit;
	int cache_miss;
};
class Cache_table_entry{
public:
	int page;
	int index;
	int age;
friend bool operator < (Cache_table_entry entry1, Cache_table_entry entry2){
	return entry1.age > entry2.age;
}

};

class Ghost_cache_entry{
public:
	Ghost_cache_entry():cache_index(-1), next(NULL), prev(NULL){
	}
	int cache_index;
	Ghost_cache_entry* next;
	Ghost_cache_entry* prev;
};

class Cache_dev{
public:
	Cache_dev(int, FILE*);

	Ghost_cache_entry * find_lru_victim();
	int get_empty_slot();
	bool is_cache_full();
	void send_io_cache(ioreq_event *);
	void make_cache_dirty(int, int);
	void push_cache(int, int);
	void replace_victim_cache(Ghost_cache_entry*);
	void write_out_trace(ioreq_event*);
	void send_cache_request(ioreq_event * curr);
	void set_replace_flag(int flag);
	void put_ghost_cache(int);
private:
	Ghost_cache_entry* is_incache(int);
	void put_ghost_cache_top(Ghost_cache_entry *);
	void put_exist_ghost_cache_top(Ghost_cache_entry *);
	void remove_exist_ghost_cache(Ghost_cache_entry *);
	int cache_size;
	int curr_size;
	int current_time;
	int free_count;
	int *free_list;
	set<int> valid_set;
	map<int,Ghost_cache_entry*> valid_map;

	priority_queue<Cache_table_entry> LRU_queue;
	FILE *outfile;
	Cache_info **cache;
	Cache_stat *cache_stat;
	Ghost_cache_entry* ghost_cache, * ghost_cache_tail; //LRU for replacement, top is always the MRU, end is always
				 //the available position of the ghost cache
	int cache_replacement_flag;
};

#endif /*__SSDSIM_CACHE__*/

